package com.example.miniofenpian.controller;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.*;
import com.example.miniofenpian.config.MinioProperties;
import com.example.miniofenpian.entity.FileCacheIno;
import com.example.miniofenpian.entity.InitTask;
import com.example.miniofenpian.entity.InitTaskVO;
import com.example.miniofenpian.util.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * 分片上传
 * <p>
 * 1. 创建一个上传任务: uploadId, 上传的每个分片对应的预上传url
 * 2. 根据预上传url,前端自己调用上传,此时与后端无关
 * 3. 合并分片,根据uploadId,合并分片
 * </p>
 *
 * @since 2022-08-22 17:47:31
 */
@RestController
@RequestMapping("/minio")
@Slf4j
public class MinioController {

    @Autowired
    private AmazonS3 amazonS3;

    @Autowired
    private MinioProperties minioProperties;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 创建一个上传任务
     *
     * @return
     */
    @PostMapping
    public Result<InitTaskVO> initTask(@RequestBody InitTask initTask) {
        // 构建上传任务参数
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType(FileUtil.extName(initTask.getFileName()));
        String finalFileName = IdUtil.fastSimpleUUID() + StrUtil.DOT + objectMetadata.getContentType();
        InitiateMultipartUploadResult initiateMultipartUploadResult = amazonS3
                .initiateMultipartUpload(new InitiateMultipartUploadRequest(minioProperties.getBucket(), finalFileName)
                        .withObjectMetadata(objectMetadata));
        // 生成上传任务id
        String uploadId = initiateMultipartUploadResult.getUploadId();
        // 缓存文件信息
        FileCacheIno fileCacheIno = new FileCacheIno();
        fileCacheIno.setUploadId(uploadId);
        fileCacheIno.setPartSize(initTask.getPartSize());
        redisTemplate.opsForValue().setIfAbsent(initTask.getMd5Str(), fileCacheIno, 10, TimeUnit.MINUTES);
        Date currentDate = new Date();
        Date expireDate = DateUtil.offsetMillisecond(currentDate, 60 * 10 * 1000);
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(minioProperties.getBucket(), finalFileName)
                .withExpiration(expireDate).withMethod(HttpMethod.PUT);
        List<String> putUploadUrls = CollUtil.newArrayList();

        // 必须从1 开始
        for (Integer i = 1; i <= initTask.getPartSize(); i++) {
            request.addRequestParameter("partNumber", i.toString());
            request.addRequestParameter("uploadId", uploadId);
            // 获取上传分片的地址
            URL preSignedUrl = amazonS3.generatePresignedUrl(request);
            putUploadUrls.add(preSignedUrl.toString());
            log.info("preSignedUrl-{}:{}", i, preSignedUrl);
        }
        return Result.ok(InitTaskVO.builder()
                .finalFileName(finalFileName).fileName(initTask.getFileName())
                .putUploadUrls(putUploadUrls).build());
    }

    /**
     * 合并分片
     *
     * @param md5Str        文件md5
     * @param finalFileName 最终文件名称
     * @return 文件全路径
     */
    @GetMapping("/merge")
    public Result merge(@RequestParam("md5Str") String md5Str, @RequestParam("finalFileName") String finalFileName) {
        if (Boolean.FALSE.equals(redisTemplate.hasKey(md5Str))) {
            return Result.error("任务不存在");
        }
        FileCacheIno fileCacheIno = (FileCacheIno) redisTemplate.opsForValue().get(md5Str);
        Assert.isTrue(ObjectUtil.isNotNull(fileCacheIno), "上传任务不存在");
        String uploadId = fileCacheIno.getUploadId();
        int partSize = fileCacheIno.getPartSize();
        ListPartsRequest listPartsRequest = new ListPartsRequest(minioProperties.getBucket(), finalFileName, uploadId);
        PartListing partListing = amazonS3.listParts(listPartsRequest);
        List<PartSummary> parts = partListing.getParts();
        Assert.isTrue(parts.size() == partSize, "分片数量不匹配");
        List<PartETag> partETags = parts.stream()
                .map(partSummary -> new PartETag(partSummary.getPartNumber(), partSummary.getETag()))
                .collect(Collectors.toList());
        CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest()
                .withUploadId(uploadId)
                .withKey(finalFileName)
                .withBucketName(minioProperties.getBucket())
                .withPartETags(partETags);
        CompleteMultipartUploadResult result = amazonS3.completeMultipartUpload(completeMultipartUploadRequest);
        return Result.ok(result.getLocation());
    }

}
